﻿// ———————————————————————–
// <copyright file="ICNETStyle.cs" company="The MITRE Corporation">
//    Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
// </copyright>
// ———————————————————————–
/////////////////////////////////////////////////////////////////////////////////////////////////
// "ICNETStyle".cs - "Methods For Writing, Reading, and Modifying the ICNET styles."
// Project: "ICNETSOA"- "ICNETDAL"
//
// Language:    C#, .NET 4.0
// Platform:    Windows 7, VS 2010
// Author:      Don McGarry The MITRE Corporation
/////////////////////////////////////////////////////////////////////////////////////////////////

// Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//
// NOTICE
// This software was produced for the U. S. Government
// under Contract No. FA8721-09-C-0001, and is
// subject to the Rights in Noncommercial Computer Software
// and Noncommercial Computer Software Documentation Clause
// (DFARS) 252.227-7014 (JUN 1995)
                
using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using ICNETDAL;

namespace ICNET_KMLStyleEngine
{
  /// <summary>
  /// This class contains methods for outputting KML feeds with the ICNET style .
  /// </summary>
  public class ICNETStyle : IXmlSerializable
  {
    #region Private Member Variables

    /// <summary>
    /// The MatchInfo object that 
    /// </summary>
    private MatchInfo matchInfo;

    /// <summary>
    /// The associated KMLStyle
    /// </summary>
    private KMLStyle kmlStyle;

    /// <summary>
    /// The associated KMLStyleMap
    /// </summary>
    private KMLStyleMap kmlStyleMap;

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the ICNETStyle class.
    /// </summary>
    public ICNETStyle()
    {
      this.matchInfo = new MatchInfo();
      this.kmlStyle = new KMLStyle();
      this.kmlStyleMap = new KMLStyleMap();
    }

    #endregion

    #region Public Accessors

    /// <summary>
    /// Gets or sets matchInfo.
    /// </summary>
    public MatchInfo Match
    {
      get { return this.matchInfo; }
      set { this.matchInfo = value; }
    }

    /// <summary>
    /// Gets or sets kmlStyle.
    /// </summary>
    public KMLStyle Style
    {
      get { return this.kmlStyle; }
      set { this.kmlStyle = value; }
    }

    /// <summary>
    /// Gets or sets KMLStyleMap.
    /// </summary>
    public KMLStyleMap StyleMap
    {
      get { return this.kmlStyleMap; }
      set { this.kmlStyleMap = value; }
    }
  
    /// <summary>
    /// Gets the KMLStyleID
    /// </summary>
    public string KMLStyleName
    {
      get 
      {
        string id = null;

        if (this.kmlStyle != null)
        {
          id = this.kmlStyle.ID;
        }

        if (this.kmlStyleMap != null)
        {
          id = this.kmlStyleMap.ID;
        }

        return id;
      }
    }

    /// <summary>
    /// Gets the friendly name for a KMLStyle
    /// </summary>
    public string KMLStyleFriendlyName
    {
      get 
      {
        string fname = null;

        if (this.kmlStyle != null)
        {
          fname = this.kmlStyle.FriendlyName;
        }

        return fname;
      }
    }

    /// <summary>
    /// Gets the MatchTypeEnum associated with this class.
    /// </summary>
    public MatchTypeEnum MatchType
    {
      get
      {
        if (this.matchInfo != null)
        {
          return this.matchInfo.MatchType;
        }
        else
        {
          return MatchTypeEnum.Unknown;
        }
      }
    }

    #endregion

    #region Public Member Functions
    
    /// <summary>
    /// Matches an item in the exposure cache with an item in the database
    /// </summary>
    /// <param name="item">The exposure cache item that wil lbe matched</param>
    /// <returns>Returns true if the exposure cache item could be matched to an item in the database, false if not.</returns>
    public bool Matched(ExposureCache item)
    {
      bool matched = false;

      if (this.matchInfo != null)
      {
        matched = this.matchInfo.IsAMatch(item);
      }

      return matched;
    }

    /// <summary>
    /// Matches an item in the exposure cache with an item in the database
    /// </summary>
    /// <param name="item">The exposure cache item that wil lbe matched</param>
    /// <returns>Returns true if the exposure cache item could be matched to an item in the database, false if not.</returns>
    public bool MatchedFromContentOrTitle(string content, string title)
    {
      bool matched = false;

      if (this.matchInfo != null)
      {
        matched = this.matchInfo.IsAMatchFromContentOrTitle(content, title);
      }

      return matched;
    }

    /// <summary>
    /// Matches an item in the exposure cache with an item in the database
    /// </summary>
    /// <param name="item">The exposure cache item that wil lbe matched</param>
    /// <returns>Returns true if the exposure cache item could be matched to an item in the database, false if not.</returns>
    public bool MatchedFromCotType(string cotType)
    {
      bool matched = false;

      if (this.matchInfo != null)
      {
        matched = this.matchInfo.IsAMatchFromCotType(cotType);
      }

      return matched;
    }

    /// <summary>
    /// Does nothing
    /// </summary>
    /// <returns>Always returns null.</returns>
    public XmlSchema GetSchema() 
    { 
      return null; 
    }

    /// <summary>
    /// Reads XML in the ICNETStyle format.
    /// </summary>
    /// <param name="reader">An XmlReader which does the reading.</param>
    public void ReadXml(XmlReader reader)
    {
      this.matchInfo = null;
      this.kmlStyle = null;
      this.kmlStyleMap = null;

      if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "ICNET_Style")
      {
        bool isEmptyElement = reader.IsEmptyElement;

        if (!isEmptyElement)
        {
          XmlReader innerReader = reader.ReadSubtree();

          this.ReadInnerXML(innerReader);

          reader.Read();
        }
      }
    }

    /// <summary>
    /// A helper method for ReadXML which reads the inner nodes. 
    /// </summary>
    /// <param name="reader">An XmlReader which does the reading.</param>
    public void ReadInnerXML(XmlReader reader)
    {
      bool isEmptyElement = false;

      while (reader.Read())
      {
        if (reader.MoveToContent() == XmlNodeType.Element)
        {
          isEmptyElement = reader.IsEmptyElement;

          if (!isEmptyElement)
          {
            if (reader.LocalName == "Style")
            {
              this.kmlStyle = new KMLStyle();

              this.kmlStyle.ReadXml(reader);

              if (!this.kmlStyle.HasAValue())
              {
                this.kmlStyle = null;
              }
            }
            else if (reader.LocalName == "StyleMap")
            {
              this.kmlStyleMap = new KMLStyleMap();

              this.kmlStyleMap.ReadXml(reader);

              if (!this.kmlStyleMap.HasAValue())
              {
                this.kmlStyleMap = null;
              }
            }
            else if (reader.LocalName == "Match_Info")
            {
              this.matchInfo = new MatchInfo();

              this.matchInfo.ReadXml(reader);

              if (!this.matchInfo.HasAValue())
              {
                this.matchInfo = null;
              }
            }
          }
        }
      }
    }

    /// <summary>
    /// Writes this object to XML
    /// </summary>
    /// <param name="writer">an XmlWriter which does the writing. </param>
    public void WriteXml(XmlWriter writer)
    {
      if (this.kmlStyle != null)
      {
        writer.WriteStartElement("Style");
        this.kmlStyle.WriteXml(writer);
        writer.WriteEndElement();
      }

      if (this.kmlStyleMap != null)
      {
        writer.WriteStartElement("StyleMap");
        this.kmlStyleMap.WriteXml(writer);
        writer.WriteEndElement();
      }
    }

    #endregion          
  }

  /// <summary>
  /// Contains methods for dealing with the ICNET styles. 
  /// </summary>
  [Serializable]
  [XmlRoot("ICNET_Styles")]
  public class ICNETStyles
  {
    /// <summary>
    /// Initializes a new instance of the ICNETStyles class.
    /// </summary>
    public ICNETStyles()
    {
      this.Styles = new List<ICNETStyle>();
    }

    /// <summary>
    /// Gets or sets the list containing all the ICNET styles.
    /// </summary>
    [XmlElement("ICNET_Style")]
    public List<ICNETStyle> Styles
    {
      get;
      set;
    }
  }

  /// <summary>
  /// Contains methods for dealing with the sorted ICNET styles. 
  /// </summary>
  public class ICNETSortedStyles
  {
    /// <summary>
    /// The SortedList of all of the ICNET styles. 
    /// </summary>
    private SortedList<double, ICNETStyle> styles;

    /// <summary>
    /// Initializes a new instance of the ICNETSortedStyles class.
    /// </summary>
    public ICNETSortedStyles()
    {
      this.styles = new SortedList<double, ICNETStyle>();
    }

    /// <summary>
    /// Gets the sorted list of ICNETStyles
    /// </summary>
    public SortedList<double, ICNETStyle> Styles
    {
      get { return this.styles; }
    }

    /// <summary>
    /// Gets the Style Name of the item in the database that matches the exposure cache. 
    /// </summary>
    /// <param name="databaseItem">The exposure cache item to be matched.</param>
    /// <returns>A string wihch represents the associated style.</returns>
    public string GetStyleIdFromMatch(ExposureCache databaseItem)
    {
      string value = null;

      ICNETStyle style = this.GetStyleFromMatch(databaseItem);

      if (style != null)
      {
        value = style.KMLStyleName;
      }

      return value;
    }

    /// <summary>
    /// Matches an ICNETStyle with an item from the exposure cache.
    /// </summary>
    /// <param name="databaseItem">The exposure cache item to be matched.</param>
    /// <returns>The matching ICNETStyle</returns>
    public ICNETStyle GetStyleFromMatch(ExposureCache databaseItem)
    {
      ICNETStyle style = null;

      foreach (KeyValuePair<double, ICNETStyle> sortedStyle in this.styles)
      {
        ICNETStyle temp = sortedStyle.Value;

        if (temp.MatchType != MatchTypeEnum.Default && temp.Matched(databaseItem))
        {
          style = temp;
          break;
        }
        else if (style == null && (@"#" + temp.KMLStyleName) == KMLStyleEngine.DefaultStyleId)
        {
            style = temp;
        }
      }

      return style;
    }

    /// <summary>
    /// Matches an ICNETStyle with an item from the exposure cache.
    /// </summary>
    /// <param name="databaseItem">The exposure cache item to be matched.</param>
    /// <returns>The matching ICNETStyle</returns>
    public ICNETStyle GetStyleFromContentOrTitle(string content, string title)
    {
      ICNETStyle style = null;

      foreach (KeyValuePair<double, ICNETStyle> sortedStyle in this.styles)
      {
        ICNETStyle temp = sortedStyle.Value;

        if (temp.MatchType != MatchTypeEnum.Default && temp.MatchedFromContentOrTitle(content, title))
        {
          style = temp;
          break;
        }
        else if (style == null && (@"#" + temp.KMLStyleName) == KMLStyleEngine.DefaultStyleId)
        {
          style = temp;
        }
      }

      return style;
    }

    /// <summary>
    /// Matches an ICNETStyle with an item from the exposure cache.
    /// </summary>
    /// <param name="databaseItem">The exposure cache item to be matched.</param>
    /// <returns>The matching ICNETStyle</returns>
    public ICNETStyle GetStyleFromCoTType(string cotType)
    {
      ICNETStyle style = null;

      foreach (KeyValuePair<double, ICNETStyle> sortedStyle in this.styles)
      {
        ICNETStyle temp = sortedStyle.Value;

        if (temp.MatchType != MatchTypeEnum.Default && temp.MatchedFromCotType(cotType))
        {
          style = temp;
          break;
        }
        else if (style == null && (@"#" + temp.KMLStyleName) == KMLStyleEngine.DefaultStyleId)
        {
          style = temp;
        }
      }

      return style;
    }

    /// <summary>
    /// Finds the style with the specified ID in the list
    /// </summary>
    /// <param name="id">The KML name for the desired style. If the ID has a leading "#", it will be ignored whle searching.</param>
    /// <returns>Returns the style or null if it cannot be found</returns>
    public ICNETStyle GetStyleFromId(string id)
    {
        if (id.StartsWith("#"))
        {
            id = id.Substring(1);
        }

        foreach (var sortedStyle in this.styles)
        {
            if (sortedStyle.Value.KMLStyleName == id)
            {
                return sortedStyle.Value;
            }
        }

        return null;
    }
  }
}
